package com.ccl.iot.privates;

import com.ccl.iot.R;
import com.ccl.iot.publics.Messager;
import com.ccl.iot.publics.UIUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Set;

public class ObjectStorer {
    public final static String KFILE_SUFFIX = ".obj";

    /*
     * 	Static functions
     */
    public static boolean Save(Storable aObject, String aFile) {
        if (aObject != null && aFile != null) {
            File file = new File(aFile);
            ObjectOutputStream tOut = null;
            try {
                File parentFile = file.getParentFile();
                if (parentFile != null && !parentFile.exists()) {
                    parentFile.mkdirs();
                }
                tOut = new ObjectOutputStream(new FileOutputStream(aFile));
                tOut.writeObject(aObject);
                tOut.close();
                return true;
            } catch (Exception e) {
                if (file.exists()) {
                    file.delete();
                }
                UIUtils.postTaskSafely(new Runnable() {

                    @Override
                    public void run() {
                        Messager.Show(R.string.Error_AddTriggerFailed);
                    }
                });
                e.printStackTrace();
            } finally {
                if (tOut != null) {
                    try {
                        tOut.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return false;
    }

    public static Object Load(String aFile) {
        Object tObject = null;
        if (aFile != null) {
            ObjectInputStream tIn = null;
            try {
                tIn = new ObjectInputStream(new FileInputStream(aFile));
                tObject = tIn.readObject();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (tIn != null) {
                    try {
                        tIn.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
        return tObject;
    }

    /*
     * 	Non-static functions
     */
    private String iPath = null;
    private Object iSyncLock = new Object();

    private HashMap<StorableObject, String> iObjects = null;

    public ObjectStorer(String aPath) {
        iPath = aPath;

        loadAll();
    }

    private FilenameFilter iFileFilter = new FilenameFilter() {
        @Override
        public boolean accept(File dir, String filename) {
            return filename != null && filename.endsWith(KFILE_SUFFIX);
        }
    };

    private void loadAll() {
        if (iPath != null) {
            File tDir = new File(iPath);
            if (tDir.exists() && tDir.isDirectory()) {
//                String tFiles[] = tDir.list(iFileFilter);
                String tFiles[] = tDir.list();

                if (tFiles != null) {
                    for (int i = 0; i < tFiles.length; i++) {
                        String tFile = iPath + File.separator + tFiles[i];

                        Object tObject = Load(tFile);
                        if (tObject != null && tObject instanceof StorableObject) {
                            add((StorableObject) tObject, tFile);
                        }
                    }
                }
            }
        }
    }

    public StorableObject[] getList() {
        if (iObjects != null) {
            synchronized (iSyncLock) {
                Set<StorableObject> tKeys = iObjects.keySet();

                if (tKeys != null) {
                    int tCount = tKeys.size();

                    if (tCount > 0) {
                        StorableObject tObjects[] = new StorableObject[tCount];

                        tKeys.toArray(tObjects);

                        return tObjects;
                    }
                }
            }
        }

        return null;
    }

    private String getFileName(StorableObject aObject) {
        if (aObject != null) {
            int tCode = aObject.hashCode();
            String tName;

            do {
                tName = iPath + File.separator + tCode + KFILE_SUFFIX;
                tCode++;
            } while (new File(tName).exists());

            return tName;
        }

        return null;
    }

    public boolean add(StorableObject aObject, String aFile) {
        if (aObject != null) {
            if (iObjects == null) {
                iObjects = new HashMap<StorableObject, String>();
            } else {
                synchronized (iSyncLock) {
                    if (iObjects.containsKey(aObject)) {
                        						return false;
                        //========modify start=============
//                        return true;    //Test
                        //========modify end=============
                    }
                }
            }

            if (aFile == null) {
                aFile = getFileName(aObject);
            }
            if (new File(aFile).exists() || Save(aObject, aFile)) {
                iObjects.put(aObject, aFile);
                aObject.setOnObjectChangeListener(iObjectChangeListener);
                return true;
            } else {
                return false;
            }
        }

        return false;
    }

    public boolean add(StorableObject aObject) {
        return add(aObject, null);
    }

    public boolean remove(StorableObject aObject) {
        if (iObjects != null && aObject != null) {
            synchronized (iSyncLock) {
                String tFile = iObjects.get(aObject);

                if (tFile != null) {
                    if (iObjects.remove(aObject) != null) {
                        aObject.setOnObjectChangeListener(null);

                        return new File(tFile).delete();
                    }
                }
            }
        }

        return false;
    }

    private StorableObject.OnObjectChangeListener iObjectChangeListener = new Storable.OnObjectChangeListener() {
        private InvokeBuffer<Storable> iInvoker = new InvokeBuffer<Storable>("Object change commit", 1000, new InvokeBuffer.InvokeDelegate<Storable>() {
            @Override
            public void onInvoke(Storable aObject) {
                if (iObjects != null) {
                    String tFile = iObjects.get(aObject);

                    if (!Save(aObject, tFile)) {
                        iObjects.remove(aObject);
                    }
                }
            }
        });

        @Override
        public void onObjectChange(Storable aObject) {
            if (aObject != null) {
                iInvoker.put(aObject);
            }
        }
    };
}
